package ack

import (
	"context"
	"fmt"
	"github.com/brocaar/chirpstack-api/go/v3/gw"
	"github.com/brocaar/lorawan"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	"network/v3/internal/storage"
)

var (
	errAbort = errors.New("abort")
)

type ackContext struct {
	ctx context.Context

	DB                  sqlx.Ext
	Token               uint16
	DownlinkTXAck       *gw.DownlinkTXAck
	DownlinkTXAckStatus gw.TxAckStatus
	DownlinkFrame       *storage.DownlinkFrame
	DownlinkFrameItem   *gw.DownlinkFrameItem
	DeviceSession       storage.DeviceSession
	DeviceProfile       storage.DeviceProfile
	DeviceQueueItem     storage.DeviceQueueItem
	MHDR                lorawan.MHDR
	MACPayload          *lorawan.MACPayload
}

// HandleDownlinkTXAck handles the given downlink TX acknowledgement.
func HandleDownlinkTXAck(ctx context.Context, downlinkTXAck *gw.DownlinkTXAck) error {
	var ackStatus gw.TxAckStatus

	if len(downlinkTXAck.Items) == 0 {
		if downlinkTXAck.Error == "" {
			ackStatus = gw.TxAckStatus_OK
		} else {
			if val, ok := gw.TxAckStatus_value[downlinkTXAck.Error]; ok {
				ackStatus = gw.TxAckStatus(val)
			} else {
				return fmt.Errorf("invalid ack error: %s", downlinkTXAck.Error)
			}
		}
	} else {
		for _, downlinkTxAckItem := range downlinkTXAck.Items {
			ackStatus = downlinkTxAckItem.Status
			if ackStatus == gw.TxAckStatus_OK {
				break
			}
		}
	}

	actx := ackContext{
		ctx:                 ctx,
		DB:                  storage.DB(),
		DownlinkTXAck:       downlinkTXAck,
		DownlinkTXAckStatus: ackStatus,
	}

	return RunTasks(&actx)
}

func RunTasks(actx *ackContext) error {
	for _, task := range handleDownlinkTXAckTasks {
		if err := task(actx); err != nil {
			if err == errAbort {
				return nil
			}
			return err
		}
	}

	return nil
}
